package com.pj.lib.auraServer.components.connection;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.SQLException;
import java.util.ArrayList;



import com.pj.lib.auraServer.app.Server;
import com.pj.lib.auraServer.app.ServerComponent;
import com.pj.lib.auraServer.app.listeners.OnAuraEvent;
import com.pj.lib.auraServer.app.properties.Properties;
import com.pj.lib.auraServer.app.properties.ServerProperties;

import com.pj.lib.auraServer.exceptions.AuraServerException;


import com.pj.lib.auraServer.utils.Log;





abstract public class NetworkManager extends ServerComponent implements Runnable{

	protected static final String FUNCTION_PREFIX_GET = "handleGet";
	protected static final String FUNCTION_PREFIX_SEND = "handleSend";
	
	private ArrayList<ClientThread> mClients = new ArrayList<ClientThread>();
	
    
    int numConnections;

    
    private boolean mustQuit = false;
    private ServerSocket ss = null;
    
	
    public NetworkManager(Server server) throws ClassNotFoundException, SQLException, AuraServerException, IOException {
    	super(server);
    	Log.i(this, "Starting server on port " + getServerProperties().PORT);
    	
    	// get ready to listen
		ss = new ServerSocket( getServerProperties().PORT);
		
    	// the constructor starts a background thread
    	new Thread( this ).start();

		addOnAuraEvent(new OnAuraEvent() {
			
			@Override
			public void temporaryLeftAura(int acId, int asuId, String action) {
				if(action != null && !action.equals("")) 
					sendActionToUser(asuId, acId, action);
			}
			
			@Override
			public void returnFromTemporaryLeftAura(int acId, int asuId, String action) {
				if(action != null && !action.equals("")) 
					sendActionToUser(asuId, acId, action);
				
			}
			
			@Override
			public void leftAura(int acId, int asuId, String action) {
				if(action != null && !action.equals("")) 
					sendActionToUser(asuId, acId, action);
			}
			
			@Override
			public void enteredAura(int acId, int asuId, String action) {
				if(action != null && !action.equals("")) 
					sendActionToUser(asuId, acId, action);
			}
		});
		
		
    }
    
    
	


	// this is our background thread
	public void run() {
		
		try {
			
			
			
			while( !mustQuit ) {
				// give out some debugging info
				Server.debug( "Listening on "+getServerProperties().PORT);
				// wait for an incoming connection
				Socket s = ss.accept();
				// record that we got another connection
				numConnections++;
				// more debugging info
				Server.debug( "Got connection on "+s );
				// process the connection -- this is implemented
				// by the subclass
				
				ClientThread newThread = getNewClientThread(s);
				newThread.start();
				
				synchronized(mClients) {
					mClients.add(newThread);
				}
				//handleConnection( s );
			}
		} catch( IOException ie ) {
			Server.debug( ie.toString() );
		} catch (ClassNotFoundException e) {
			Server.debug( e.toString() );
		} catch (SQLException e) {
			Server.debug( e.toString() );
		} catch (AuraServerException e) {
			Server.debug( e.toString() );
		}
		Server.debug( "Shutting down "+ss );
		cleanUp();
	}
	
	protected ServerProperties getServerProperties() {
		return (ServerProperties) getServer().getProperties(Properties.PROP_SERVER);
	}
	
	public synchronized ArrayList<ClientThread> getClients() {
		return mClients;
	}
	
	protected void sendActionToUser(int asuId, int acId, String action) {
		ArrayList<ClientThread> clients = getClients();
		for(ClientThread client: clients) {
			if(client.getUserId() == asuId) {
				client.handleSendAuraEvent(asuId, acId, action);
				break;
			}
		}
		
	}

	
	// get the ClientThread object
	abstract public ClientThread getNewClientThread(Socket s) throws ClassNotFoundException, SQLException, AuraServerException;
	
	
	
	/**
     * Funkcja pozwala na usunięcie wątku połączenia z tablicy połączeń.
     * 
     * @param log       login użytkownika którego chcemy usunąć z tablicy
     * @param s         referencja do obiektu Socketa danego użytkownika
     */
    public final void removeConnection(ClientThread client) {
        synchronized( mClients ) {
            mClients.remove(client);
            
	        try {
	            client.getSocket().close();
	        }   catch( IOException ie ) {
	            System.out.println( "Error closing "+client.getSocket());
	             ie.printStackTrace();
	        }
        }
    }
	

	
	// tell the thread to stop accepting connections
	public void close() {
		mustQuit = true;
	}
	// Put any last-minute clean-up stuff in here
	protected void cleanUp() {
		
	}
	

	
	
	


}
